﻿/*
 * Name:        PlayField
 * Description: An entity that handles playing field and pickups
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;

using Gyvate.Util;
using Gyvate.Worldspawn;
using Gyvate.Entities;

namespace Gyvate.Field
{
    internal class PlayField
    {
        private Game m_game;

        private Dimensions2d m_dimensions;
        private PrecachedSprite m_wallSprite;

        private Dictionary<Vector2d, BaseEntity> m_pickupList;
        private Dictionary<Vector2d, BaseEntity> m_drawList;

        private Spawn m_spawnPoint;

        public Dimensions2d GetDimensions() { return m_dimensions; }                        //Gets field dimensions
        public Vector2d GetPos() { return m_dimensions.pos; }                               //Gets field origin
        public Vector2d GetSize() { return m_dimensions.size; }                             //Gets field sizeMainPanel
        public Dictionary<Vector2d, BaseEntity> GetPickupList() { return m_pickupList; }    //Gets pickup list
        public Spawn GetSpawnPoint() { return m_spawnPoint; }                               //Gets head spawn location
        public Dictionary<Vector2d, BaseEntity> GetDrawList() { return m_drawList; }        //Gets draw list

        /*
         * Constructor
         * Creates a field with given sizeMainPanel, at (0, 0) origin
         */
        public PlayField(Game game, Vector2d size)
        {
            m_game = game;
            m_dimensions = new Dimensions2d(new Vector2d(), size);
            Init();
        }

        /*
         * Resets play field
         */
        public void Reset()
        {
            PrecacheWorld();
            RebuildWorld();
        }

        /*
         * Constructor
         * Creates a field with given sizeMainPanel, at specified origin
         */
        public PlayField(Game game, Vector2d pos, Vector2d size)
        {
            m_game = game;
            m_dimensions = new Dimensions2d(pos, size);
            Init();
        }

        /*
         * Initializes playing field
         */
        private void Init()
        {
            m_drawList = new Dictionary<Vector2d, BaseEntity>();
        }

        /*
         * Rebuilds world
         */
        public void RebuildWorld()
        {
            m_pickupList = new Dictionary<Vector2d, BaseEntity>(m_drawList);
            SpawnFood();
        }

        public void ClearWorld()
        {
            m_pickupList = null;
        }

        /*
         * Precaches world
         */
        private void PrecacheWorld()
        {
            m_game.PrecacheSprite(Constants.SPRITE_WALL);

            m_wallSprite = m_game.GetPrecachedSprite(Constants.SPRITE_WALL);
        }

        /*
         * Draws play field walls
         */
        internal void Draw(bool toConsole = false)
        {
            if (!toConsole)
            {
                for (int i = 0; i < GetSize().x + 1; i++)
                    for (int j = 0; j < GetSize().y + 1; j++)
                    {
                        if (i == 0 || i == GetSize().x)
                        {
                            DrawWallSprite(new Vector2d(i, j));
                            continue;
                        }

                        if (j == 0 || j == GetSize().y)
                            DrawWallSprite(new Vector2d(i, j));
                    }
            }
        }

        /*
         * Draws wall sprite at specified position
         */
        private void DrawWallSprite(Vector2d pos)
        {
            m_game.GetGraphics().DrawImage(m_wallSprite.GetBitmap(), new Point(pos.x * m_wallSprite.GetImage().Width, pos.y * m_wallSprite.GetImage().Height));
        }

        /*
         * Spawns a food pickup at random origin
         */
        public void SpawnFood()
        {
            if (m_game.GetGameRules() == null || m_game.GetCollisionManager() == null)
                return;

            int emptySlots = (Constants.FIELD_HEIGHT+1) * (Constants.FIELD_WIDTH+1);

            emptySlots -= m_pickupList.Count;
            emptySlots -= m_game.GetSnake().GetBodyList().Count;

            if (emptySlots < 0)
            {
                m_game.SendEvent(EventList.EVENT_WIN);
                return;
            }

            bool hasWalls = m_game.GetGameRules().IsWallSolid();
            bool canSpawn = false;
            Vector2d foodPos = new Vector2d();

            Vector2d min = new Vector2d(0, 0);
            Vector2d max = new Vector2d(Constants.FIELD_WIDTH, Constants.FIELD_HEIGHT);

            if (hasWalls)
            {
                min.x++;
                min.y++;
                max.x--;
                max.y--;
            }

            do
            {
                foodPos.x = m_game.GetRandomNumber(min.x, max.x);
                foodPos.y = m_game.GetRandomNumber(min.y, max.y);

                if (m_pickupList.ContainsKey(foodPos))
                    continue;

                canSpawn = !m_game.GetCollisionManager().CheckBodyCollisionByPostion(foodPos);
            } while (!canSpawn);

            Food newFood = new Food(m_game, foodPos);
            newFood.SetSprite(Constants.SPRITE_FOOD);
            m_pickupList.Add(foodPos, newFood);
        }

        /*
         * Draws pickups
         */
        public void DrawPickups()
        {
            if (m_pickupList != null)
                foreach (KeyValuePair<Vector2d, BaseEntity> item in m_pickupList)
                    item.Value.Draw();

            if (m_game.IsDebug() && m_drawList != null)
                foreach (KeyValuePair<Vector2d, BaseEntity> item in m_drawList)
                    item.Value.Draw();
        }

        /*
         * Removes tile in the specified origin
         */
        public void DebugRemoveEntity(Vector2d pos)
        {
            m_drawList.Remove(pos);
        }

        /*
         * Draws specified entity at specified location
         * Used for map building
         */
        public void DebugPutEntity(Vector2d pos, TileList tile)
        {
            //if (!m_game.IsDebug())
            //    return;

            switch (tile)
            {
                default:
                    break;
                case TileList.TILE_SNAKESPAWN:
                    DebugCreateSpawnPoint(pos);
                    return;
                case TileList.TILE_SNAKESPAWN_90:
                    DebugCreateSpawnPoint(pos, 90);
                    return;
                case TileList.TILE_SNAKESPAWN_180:
                    DebugCreateSpawnPoint(pos, 180);
                    return;
                case TileList.TILE_SNAKESPAWN_270:
                    DebugCreateSpawnPoint(pos, 270);
                    return;
            }

            BaseEntity ent = null;
            if (!m_drawList.TryGetValue(pos, out ent) && tile == TileList.TILE_NONE)
                return;

            if (ent != null)
                if (ent.GetTileType() == tile)
                    return;

            if (ent != null)
            {
                DebugRemoveEntity(pos);
                ent = null;
            }

            /*
             * This doesn't look good, but time is running out, and I don't feel like adding sprite sheets
             */
            switch (tile)
            {
                case TileList.TILE_NONE:
                default:
                    break;
                case TileList.TILE_WALL:
                    ent = (BaseEntity)new Wall(m_game, pos);
                    break;
                case TileList.TILE_FOOD:
                    ent = (BaseEntity)new Food(m_game, pos);
                    break;
                case TileList.TILE_SNAKESPAWN:
                    ent = (BaseEntity)new Spawn(m_game, pos);
                    break;
            }

            if (ent != null)
                m_drawList.Add(pos, ent);
        }

        /*
         * Creates a single spawn point for the snake to be created in
         */
        private void DebugCreateSpawnPoint(Vector2d pos, int rotation = 0)
        {
            DebugRemoveEntity(pos);

            if (m_spawnPoint != null)
                DebugRemoveEntity(m_spawnPoint.GetPos());

            m_spawnPoint = new Spawn(m_game, pos, rotation);

            m_drawList.Add(pos, m_spawnPoint);
        }

        /*
         * Places (or removes) a specified entity at given origin if possible
         */
        private void PlaceEntity(Vector2d pos, TileList tile)
        {
            BaseEntity ent = null;
            if (!m_pickupList.TryGetValue(pos, out ent) && tile == TileList.TILE_NONE)
                return;

            if( ent != null )
                if (ent.GetTileType() == tile)
                    return;

            switch (tile)
            {
                case TileList.TILE_NONE:
                    m_pickupList.Remove(pos);
                    ent = null;
                    break;
                case TileList.TILE_WALL:
                    ent = (BaseEntity)new Wall(m_game, pos);
                    break;
                case TileList.TILE_FOOD:
                    ent = (BaseEntity)new Food(m_game, pos);
                    break;
            }

            if( ent != null )
                m_pickupList.Add(pos, ent);
        }
    }
}
